/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/xia-chu/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <malloc.h>
#include <signal.h>
#include <atomic>
#include <iostream>
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/TimeTicker.h"
#include "Poller/Timer.h"
#include "Extension/H264.h"
#ifdef ENABLE_GPERFCHECK
#include<gperftools/heap-profiler.h>
#endif
using namespace std;
using namespace toolkit;

static inline int get_memory_by_pid(pid_t pid) {
    FILE* fd;
    char line[1024] = {0};
    char virtual_filename[32] = {0};
    char vmrss_name[32] = {0};
    int vmrss_num = 0;
    sprintf(virtual_filename, "/proc/%d/status", pid);
    fd = fopen(virtual_filename, "r");
    if(fd == NULL) {
        cout << "open " << virtual_filename << " failed" << endl;
        abort();
    }
    // VmRSS line is uncertain
    for(int i = 0 ; ; i++) {
        if(fgets(line,sizeof(line),fd) == NULL) {
            break;
        }
        if(strstr(line, "VmRSS:") != NULL) {
            sscanf(line, "%s %d", vmrss_name, &vmrss_num);
            break;
        }
    }
    fclose(fd);
    return vmrss_num;
}
    
static inline int get_machine_memory() {
    const char* virtual_filename = "/proc/meminfo";
    FILE* fd;
    char line[1024] = {0};
    fd = fopen(virtual_filename, "r");
    if(fd == NULL) {
        cout << "open " << virtual_filename << " failed" << endl;
        abort();
    }
    char vmrss_name[32];
    int machine_memory;
    // MemTotal line is uncertain
    for(int i = 0 ; ; i++) {
        if(fgets(line,sizeof(line),fd) == NULL) {
            break;
        }
        if(strstr(line, "MemTotal:") != NULL) {
            sscanf(line, "%s %d", vmrss_name, &machine_memory);
            break;
        }
    }
    fclose(fd);
    return machine_memory;
}
#define USE_MULTI_THREAD
class TransThread {
    public:
        typedef std::shared_ptr<TransThread> Ptr;
        TransThread();
         ~TransThread();
        void start();
        void stop();
        void wait();
        void PushFrame(const Frame::Ptr &frame_in);
    private: 
        void run();
    private:  
        
        std::list<Frame::Ptr> _frameCached;
        std::atomic<int> _frame_counter = {0};
        #ifdef USE_MULTI_THREAD  
        thread_group _thread_group;
        semaphore _sem;
        mutable mutex  _frameCachedMtx;
        std::atomic<bool> _running = {true};
        #endif
};
#define CULMULATED_FRAME_COUNT 100
TransThread::TransThread(){
}
TransThread::~TransThread() {
    PushFrame(nullptr);
    wait();
}
void TransThread::PushFrame(const Frame::Ptr &frame_in){
    _frame_counter++;
    #ifdef USE_MULTI_THREAD   
    _frameCachedMtx.lock();
    _frameCached.push_back(frame_in);
    //printf("送进去一帧数据,当前大小:%lu\n",_frameCached.size());
    _frameCachedMtx.unlock();
    if(_frame_counter.load() % CULMULATED_FRAME_COUNT == 0) { 
        _sem.post();
    }else{
        printf("[%d]送进去一帧数据,当前大小:%lu\n",__LINE__,_frameCached.size());
    }
    #else
    _frameCached.push_back(frame_in);
    //printf("送进去一帧数据,当前大小:%lu\n",_frameCached.size());
    if(_frame_counter.load() % CULMULATED_FRAME_COUNT == 0) {
        _frameCached.clear();
    }else{
        printf("[%d]送进去一帧数据,当前大小:%lu\n",__LINE__,_frameCached.size());
    }
    #endif
}
void TransThread::start() {
#ifdef USE_MULTI_THREAD
    auto thrd = _thread_group.create_thread(bind(&TransThread::run, this));
    SetThreadName(thrd,"TransThread");//设置线程名称为转码线程
#endif
}
void TransThread::stop(){
#ifdef USE_MULTI_THREAD
    _running = false;
    _sem.post();
    wait();
#endif
}
void TransThread::wait() {
#ifdef USE_MULTI_THREAD   
    _thread_group.join_all();
#endif
}
void TransThread::run() {
#ifdef USE_MULTI_THREAD       
    while(_running.load()){
        Frame::Ptr frame = nullptr;
        _frameCachedMtx.lock();
        if(_frameCached.size() <= 0) {
            printf("等待帧,当前大小:%lu\n",_frameCached.size());
            _frameCachedMtx.unlock();
            _sem.wait();
            continue;
        }else{
            //printf("拿到帧,当前大小:%lu\n",_frameCached.size());
            frame = _frameCached.front();
            _frameCached.pop_front();
            _frameCachedMtx.unlock();
        }
        if(frame) {
             
        }else{
            //视为退出信号
            InfoL << "quit  h265 transcode thread";
            break;
        }
    }
#endif
}
#define FRAME_SIZE 1318000
//使用gperftools分析工具的时候,需要特别注意一点,gperftools本身有缓存功能,释放的内存不会马上被回收,容易造成判断上的误解！
int main(int argc,char * argv[]) {
    mallopt(M_TRIM_THRESHOLD, -1);
    #ifdef ENABLE_GPERFCHECK
    /**
        /opt/ThirdParty/opensource/gperftools-2.9.1/bin/pprof test_Frame   --base=test_Frame.0001.heap  test_Frame.0003.heap  --pdf > test_Frame.pdf
        */
    HeapProfilerStart(argv[0]);
    #endif
    //设置日志
    Logger::Instance().add(std::make_shared<ConsoleChannel>());
    Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());

    TransThread thrd;
    thrd.start();
    Ticker ticker0;
    Timer::Ptr timer0 = std::make_shared<Timer>(0.1f,[&](){
        int machine_memory = get_machine_memory();
        TraceL << "机器内存:" << get_machine_memory() << ",进程内存:" << get_memory_by_pid(getpid());  
        char *data = (char *)malloc(FRAME_SIZE);
        int bytes = FRAME_SIZE;
        auto frame = std::make_shared<mediakit::H264FrameNoCacheAble>((char *) data, bytes, 1000, 1000, 4, 0);
        thrd.PushFrame(Frame::getCacheAbleFrame(frame));
        free(data);
        return true;
    }, nullptr);

   
    //退出程序事件处理
    static semaphore sem;
    signal(SIGINT, [](int) { sem.post();});// 设置退出信号
    sem.wait();
    thrd.stop();
    #ifdef ENABLE_GPERFCHECK
    HeapProfilerStop();
    #endif
    return 0;
}
